<?php

/**#@+
 * Error Message Constants
 *
 * These constants should be used with messages passed to Debug_Error to specify
 * the sverity of an message and what if any action should be taken.
 */
define('W_LOG_EMERG',    0); /** System is unusable         */
define('W_LOG_ALERT',    1); /** Immediate action required  */
define('W_LOG_CRIT',     2); /** Critical conditions        */
define('W_LOG_ERR',      3); /** Error conditions           */
define('W_LOG_WARNING',  4); /** Warning conditions         */
define('W_LOG_NOTICE',   5); /** Normal but significant     */
define('W_LOG_INFO',     6); /** Informational              */
define('W_LOG_DEBUG',    7); /** Debug-level messages       */
/**#@-*/

// File Dependencies
using('Library.DateTime');
using('Library.DataTypes.Array.Conversion');

/**
 * Warlock Error Handler
 *
 * Handles all error and debug messages from all the core objects
 * and modules.
 *
 * Warlock is free software. This version may have been modified pursuant
 * to the GNU General Public License, and as distributed it includes or
 * is derivative of works licensed under the GNU General Public License or
 * other free or open source software licenses.
 *
 * @author     David Cole <neophyte@sourcetutor.com>
 * @package    warlock
 * @subpackage library
 * @license    GNU/GPL v2
 *
 */
class Debug_Error extends Object
{
    /**
     * The default priority to use when logging an event.
     *
     * @var integer
     * @access private
     */
    var $_priority = W_LOG_DEBUG;

    /**
     * The microtime the error handler was first instanced.
     *
     * @var int
     * @access private
     */
    var $_startTime;

    /**
     * An array of all logged events
     *
     * @var array
     * @access private
     */
    var $_messages = array();

    /**
     * Class Contructor
     */
    function __construct()
    {
        // Add class instance to vault
        Vault::Add('Debug_Error', &$this);
    }

    /**
     * Abstract implementation of the log() method.
     */
    function Log($message, $priority = null)
    {
        // If a priority hasn't been specified, use the default value.
        if ($priority === null)
            $priority = $this->_priority;

		// Append log entry to log array
		$this->_messages[] = array('priority'  => $priority,
		                           'timestamp' => DateTime_MicroTime(true) - W_START,
		                           'message'   => $message
		);


		if ($priority <= W_LOG_CRIT)
		    $this->_TerminatePageExecution();

		return false;
	}

	function _TerminatePageExecution()
	{
	    if (file_exists(($file = W_ENV_PATH.'/Themes/Error/default.tpl')))
	    {
	        /*
	         * We're in luck and can make a niceley formatted
	         * page for the error messages.
	         */

	        $template = file_get_contents($file);

	        $content = '<h2>Unrecoverable Error Encountered</h2>
                        <div class="section message">
                          <strong>Message:</strong>
                          <p>'.$this->_messages[count($this->_messages) - 1]['message'].'</p>
                        </div>
                        <div class="section source">
                          <h3>Debug</h3>
                          <p>
                            <div class="code">';

	        for ($i = 0; $i < count($this->_messages); $i++)
	            $content .= '<div style="display: table-row;"><p style="display: table-cell; width: 150px;">'.number_format($this->_messages[$i]['timestamp'], 6, null, '').' - '.'<span style="">'.$this->_PriorityToString($this->_messages[$i]['priority']).'</p><p display: table-cell; style="background-color: #FFFFFF; margin: 2px; padding: 1px;">'.$this->_messages[$i]['message']."</p></div>\n";

	        $content .= '   </div>
                          </p>';

	        $content .= '<h3>Backtrace</h3>
                         <p>
                           <div class="code">';

	        $backtrace = debug_backtrace();

	        foreach ($backtrace as $call)
	            if ($call['function'] != 'errorhandler')
	                $content .= str_replace(W_ENV_PATH, '', $call['file']).':'.$call['line'].' - '.((isset($call['class']))?$call['class'].$call['type']:'').$call['function'].'('.((isset($call['args']))?implode(',', $call['args']):'').');'."<br />\n";

	        $content  .='  </div>
	                     </p>
	                   </div>';

	        $template = str_replace('{title}', '500 Internal Server Error in ['.$_SERVER['REQUEST_URI'].']', $template);
	        $template = str_replace('{content}', $content, $template);

	        header($_SERVER['SERVER_PROTOCOL'].' 500 Internal Server Error');
	        die ($template);
	    }
	    else
	    {
	        // System must be really skrewed :/ dump what we know to output
	        header($_SERVER['SERVER_PROTOCOL'].' 500 Internal Server Error');
	        print_r($this->_messages);
	    }
	}

    /**
     * Get Log Messages
     *
     * Grabs all the log messages from {@link _messages}
     */
    function messages()
    {
        return array2xml('Debug:Error', $this->_messages);
    }

    /**
     * Returns the string representation of a PEAR_LOG_* integer constant.
     *
     * @param int $priority     A SEP_LOG_* integer constant.
     * @return string           The string representation of $level.
     */
    function _PriorityToString($priority)
    {
        static $levels = array(
                        W_LOG_EMERG   => 'Emergency',
                        W_LOG_ALERT   => 'Alert',
                        W_LOG_CRIT    => 'Critical',
                        W_LOG_ERR     => 'Error',
                        W_LOG_WARNING => 'Warning',
                        W_LOG_NOTICE  => 'Notice',
                        W_LOG_INFO    => 'Info',
                        W_LOG_DEBUG   => 'Debug'
                    );

        return $levels[$priority];
    }

    function _PriorityToColor($priority)
    {
        static $levels = array(
                        W_LOG_EMERG   => 'Emergency',
                        W_LOG_ALERT   => 'Alert',
                        W_LOG_CRIT    => 'Critical',
                        W_LOG_ERR     => 'Error',
                        W_LOG_WARNING => 'Warning',
                        W_LOG_NOTICE  => 'Notice',
                        W_LOG_INFO    => 'Info',
                        W_LOG_DEBUG   => 'Debug'
                    );

        return $levels[$priority];
    }

    /**
     * Returns the current default priority.
     *
     * @return integer  The current default priority.
     * @access  public
     */
    function getPriority()
    {
        return $this->_priority;
    }

    /**
     * Sets the default priority to the specified value.
     *
     * @param   integer $priority   The new default priority.
     * @access  public
     */
    function setPriority($priority)
    {
        $this->_priority = $priority;
    }

    /**
     * Returns all logged events to the point the method is called
     *
     * @return  array
     * @access  public
     */
    function GetLog()
    {
        return $this->_messages;
    }
}

function ErrorHandler($code, $message, $file, $line)
{
    // Make sure we have an instance of the ErrorHandler
    static $error;

    if (!is_object($logger))
        $error = &Vault::get('Debug_Error');

    // Map the PHP error to a warlock error handler priority
    switch ($code)
    {
        case E_WARNING:
        case E_USER_WARNING:
            $priority = W_LOG_WARNING;
            break;
        case E_NOTICE:
        case E_USER_NOTICE:
            $priority = W_LOG_NOTICE;
            break;
        case E_ERROR:
        case E_USER_ERROR:
            $priority = W_LOG_CRIT;
            break;
        default:
            $priority = W_LOG_INFO;
    }

    // Log the error message
    $error->Log(str_replace(W_ENV_PATH, '', $file).':'.$line.' - '.$message, $priority);
}

set_error_handler('ErrorHandler');

?>
